Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render project workflows on the server #1809

Merged
merged 13 commits into from
Jan 8, 2021
Merged

Conversation

eatyourgreens
Copy link
Contributor

@eatyourgreens eatyourgreens commented Sep 18, 2020

Request workflows in getDefaultPageProps, then pass the workflow data down to the Hero component as a prop. Wait for user login to complete before displaying the list of workflows in the browser.

This is now based off #1823 so that getServerSideProps is consistent across branches.

Package:
app-project

Closes #1803.
Fixes #1827.

Review Checklist

General

  • Are the tests passing locally and on Travis?
  • Is the documentation up to date?

Components

Apps

  • Does it work in all major browsers: Firefox, Chrome, Edge, Safari?
  • Does it work on mobile?
  • Can you yarn panic && yarn bootstrap or docker-compose up --build and app works as expected?

Publishing

  • Is the changelog updated?
  • Are the dependencies updated for apps and libraries that are using the newly published library?

Post-merging

@eatyourgreens eatyourgreens added the enhancement New feature or request label Sep 18, 2020
@eatyourgreens
Copy link
Contributor Author

Tests are broken and the code needs re-organising, but this works, which is very promising.

@eatyourgreens eatyourgreens marked this pull request as ready for review September 21, 2020 15:50
@eatyourgreens
Copy link
Contributor Author

This is neat. NextJS serialises the project and workflows data as JSON, then requests
http://localhost.zooniverse.org:3000/_next/data/pKc0Nl5BF0VaThy5N0Po4/projects/darkeshard/engaging-the-crowds-2020.json
when you visit the home page from the Classify page.

@eatyourgreens eatyourgreens force-pushed the server-side-workflows branch 4 times, most recently from d0845d6 to 85f3518 Compare September 23, 2020 11:36
@eatyourgreens eatyourgreens requested a review from a team September 23, 2020 11:43
@eatyourgreens eatyourgreens force-pushed the server-side-workflows branch 4 times, most recently from 273053f to f43f427 Compare September 24, 2020 13:07
Copy link
Contributor

@srallen srallen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't really had index.js files hold code before, just handle import/export of modules. I don't think I would think to look there for that. I would prefer organizationally that the async requests added to the packages/app-project/pages/projects/[owner]/[project]/index.js file be put elsewhere ideally with specs. We've used folders called helpers elsewhere to contain one off functions with specs.

@eatyourgreens
Copy link
Contributor Author

I'm hoping #1823 will make things cleaner by moving the common page props, like the project, into a shared function. However, with NextJS 9.3 and up, we're going to have to add functions to individual pages. It's probably a good idea to keep those functions short. I wasn't sure how to test the page props here, without just duplicating the fetchWorkflowsHelper tests.

@eatyourgreens
Copy link
Contributor Author

eatyourgreens commented Sep 25, 2020

I've tidied up getting the page props from the request. #1823 should allow us to replace the duplicated project fetch (once in getInitialProps and once here) with something like:

const { env } = query
const { props: defaultProps } = await getDefaultPageProps({ params, query, req, res })
const language = defaultProps.initialState.project.primary_language
const { active_workflows, default_workflow } = defaultProps.initialState.project.links
const workflows = fetchWorkflowsHelper(language, active_workflows, default_workflow, env)
const props = Object.assign({}, { workflows }, defaultProps)
return { props }

@eatyourgreens
Copy link
Contributor Author

For the content pages app, I've experimented with putting the page data fetching functions under /src/screens:
https://github.com/zooniverse/front-end-monorepo/blob/master/packages/app-content-pages/src/screens/Publications/getStaticProps.js

I'm not 100% sold on that approach because I wouldn't necessarily look in src/screens for functions that handle HTTP requests. The project workflow and subject set pages also don't have components under src/screens, since they re-use ClassifyPage. Neither of those are big problems, in my mind. We could export src/screens/ProjectHomePage/getServerSideProps from pages/project/[owner]/[project]/index and src/screens/ClassifyPage/getServerSideProps from every page under pages/project/[owner]/[project]/classify

@eatyourgreens
Copy link
Contributor Author

I ended up copying the tests for getServerSideProps from the tests for fetchWorkflowsHelper, so there’s a lot of overlap between those specs.

Copy link
Contributor

@srallen srallen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking, but I don't think it's clear upon reading the WorkflowSelector code that it's contingent on the user loading state, not the workflow loading state. I think given the name of the component, one would assume it's the workflow. I think this could be better named to avoid this confusion.

Also, what happens now when the workflow fails to load? The UI has been changed to only display certain UI messaging based on user load state, not workflow, so what if the workflow request fails?

const workflows = await fetchWorkflowsHelper(language, active_workflows, default_workflow, env)
const props = { workflows }
return ({ props })
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if these requests fail? What kind of handling should these have for different scenarios, say 404 vs 500s?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. What happens at the moment? Doesn't the component display a message about workflows failing to load?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@eatyourgreens eatyourgreens Oct 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could send a project workflows error state back with the page props. The generated HTML might be cached across page refreshes, which could be a problem if it's a temporary error.

Uncaught errors on the server will generate a 500 response, but that won't be cached. The error page is customisable, so setting up a project error page might be a good first issue. That ties into having a custom 404 page too (see #1694.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NextJS recommend not handling server-side errors, although there's next-to-no official documentation about that, just discussions about best practice. vercel/next.js#11644 has some discussion about error-handling.

If we do handle errors, and send back a 200 response with a project home page, then it's worth noting that this can be indexed by search engines (as well as cached by browsers.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Next 10 adds notFound and redirect keys to the returned object from getServerSideProps. That could be useful.

Also worth noting that the workflows request can't 404. notFound would only be needed when requesting a specific resource.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with punting this issue for version 10 if it has a better API for it. Let's track in an issue?

@eatyourgreens eatyourgreens force-pushed the project-initial-props branch from 1348c8f to 079dffd Compare January 6, 2021 18:24
@eatyourgreens eatyourgreens force-pushed the server-side-workflows branch from d582ead to d78c1b7 Compare January 7, 2021 12:14
Base automatically changed from project-initial-props to master January 7, 2021 14:17
@eatyourgreens eatyourgreens force-pushed the server-side-workflows branch 2 times, most recently from 406e19e to 89728a2 Compare January 7, 2021 15:56
@eatyourgreens
Copy link
Contributor Author

eatyourgreens commented Jan 7, 2021

I've done some reading up on server-side errors in NextJS. I think we have two choices.

  1. Any uncaught error in getServerSideProps returns a 500 response and the app's error page. The default error page is a blank page saying 'An unexpected error has occurred' (see the screenshot in Project: client-side page data requests (/_next/data/ URLs) broken #1826.) NextJS recommends letting Next handle your server-side errors and set up a custom error page. This seems like the appropriate response, to me, if Panoptes is down.
  2. We wrap getServerSideProps in try… catch and return some default set of props in case of an error. In this case we'd send back a 200 response along with a page that includes an error message. That makes me slightly nervous because a 200 response can be cached by the browser and might not refresh immediately on page reload.

EDIT: 200 responses can be indexed by search engines too.

@eatyourgreens eatyourgreens requested a review from srallen January 8, 2021 12:37
eatyourgreens and others added 11 commits January 8, 2021 13:47
Request workflows for the home page in getServerSideProps, then pass the workflow data down to the Hero component as a prop.
Add the logged-in user to the workflow selector from the mobx store. Pass the user loading state down as a prop.
Pass down workflow data as a prop, without loading status.
Co-authored-by: srallen <srallen@users.noreply.github.com>
Remove the getProjectWorkflowsHelper. Get the workflow language from the project.
Mock a linked workflow for a mock request slug and test the staging and production APIs.
`loadingState` for the workflow selector doesn't really tell us what is being loaded from the API. `userReadyState` is more accurate and consistent with `subjectReadyState` used elsewhere in the code.
Add workflows prop types to the home page, and home page container. Default workflows to an empty array.
@eatyourgreens eatyourgreens force-pushed the server-side-workflows branch from b781775 to 270a674 Compare January 8, 2021 13:48
Get workflows from the project with getDefaultPageProps, so that project workflows are also available on the Classify page.
Copy link
Contributor

@srallen srallen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I'm satisfied having getServerSideProps in the index file of the pages for now. However, one recommendation I have is to at least add basic smoke unit tests for these and this would I think be sufficient to signal to another dev that there's code in these files.

Any uncaught error in getServerSideProps returns a 500 response and the app's error page. The default error page is a blank page saying 'An unexpected error has occurred' (see the screenshot in #1826.) NextJS recommends letting Next handle your server-side errors and set up a custom error page. This seems like the appropriate response, to me, if Panoptes is down.

This seems like the right choice to me too.

@github-actions github-actions bot added the approved This PR is approved for merging label Jan 8, 2021
@eatyourgreens
Copy link
Contributor Author

In #1964 I’ve figured how to customise the error message. I haven’t experimented much with it, but that might work for 500 errors too.

@eatyourgreens
Copy link
Contributor Author

One thing I haven’t figured out: how to add tests to pages. Every JS file under /pages is a page on the site. Right now, my approach to that is to imported helpers, which have tests, and run those in getServerSideProps.

@eatyourgreens
Copy link
Contributor Author

This might work. Put the page tests in /src/tests. https://medium.com/frontend-digest/testing-getserversideprops-in-nextjs-b339ebcf3401

@eatyourgreens eatyourgreens merged commit 52812c3 into master Jan 8, 2021
@eatyourgreens eatyourgreens deleted the server-side-workflows branch January 8, 2021 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved This PR is approved for merging enhancement New feature or request
Projects
None yet
2 participants